package com.meida.module.arc.provider.service.impl;

import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
import cn.afterturn.easypoi.util.WebFilenameUtils;
import cn.hutool.core.util.NumberUtil;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.meida.common.base.entity.EntityMap;
import com.meida.common.base.module.MyExportParams;
import com.meida.common.base.utils.FlymeUtils;
import com.meida.common.mybatis.base.service.impl.BaseServiceImpl;
import com.meida.common.mybatis.model.ResultBody;
import com.meida.common.mybatis.query.CriteriaQuery;
import com.meida.common.mybatis.query.CriteriaSave;
import com.meida.common.security.OpenHelper;
import com.meida.common.utils.*;
import com.meida.module.arc.client.entity.ArcCategory;
import com.meida.module.arc.client.entity.ArcField;
import com.meida.module.arc.client.entity.ArcImport;
import com.meida.module.arc.client.entity.ArcInfo;
import com.meida.module.arc.client.enums.ArcStatusEnum;
import com.meida.module.arc.client.enums.ArchiveEnumInteger;
import com.meida.module.arc.client.utils.ArcUtils;
import com.meida.module.arc.client.vo.ImportExcelMatchJson;
import com.meida.module.arc.provider.mapper.ArcImportMapper;
import com.meida.module.arc.provider.service.ArcCategoryService;
import com.meida.module.arc.provider.service.ArcFieldService;
import com.meida.module.arc.provider.service.ArcImportService;
import com.meida.module.arc.provider.service.ArcInfoService;
import com.meida.module.file.provider.oss.FileUploadUtil;
import com.meida.module.system.client.entity.SysDept;
import com.meida.module.system.provider.service.SysDeptService;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;

/**
 * excel导入记录接口实现类
 *
 * @author flyme
 * @date 2021-12-26
 */
@Service
@Transactional(rollbackFor = Exception.class)
@DS("sharding")
public class ArcImportServiceImpl extends BaseServiceImpl<ArcImportMapper, ArcImport> implements ArcImportService {

    @Autowired
    private ArcFieldService arcFieldService;

    @Autowired
    private ArcCategoryService arcCategoryService;

    @Autowired
    @Lazy
    private ArcInfoService arcInfoService;

    @Autowired
    private SysDeptService sysDeptService;


    @Override
    public ResultBody beforeAdd(CriteriaSave cs, ArcImport arcImport, EntityMap extra) {
        return ResultBody.ok();
    }



    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
    public ResultBody beforePageList(CriteriaQuery<ArcImport> cq, ArcImport arcImport, EntityMap requestMap) {
        ApiAssert.isNotEmpty("门类id不能为空",arcImport);
        ApiAssert.isNotEmpty("门类id不能为空",arcImport.getCategoryId());
        cq.lambda().eq(ArcImport::getCategoryId,arcImport.getCategoryId());
        cq.orderByDesc("import.createTime");
        return ResultBody.ok();
    }

    //TODO [微服务问题] 微服务需要将文件保存到公共存储上,不能保存在本地
    @Override
    public ResultBody fileUpload(MultipartFile file) {
        try {
            String fileBasePath = FileUploadUtil.datePath() + "/";
            String fileName = file.getOriginalFilename();
            String[] fileNameArr = fileName.split("\\.");
            if(fileNameArr.length>1){
                fileName = DateUtil.date2Str(new Date(),"yyyyMMddHHmmssSSS")+"."+fileNameArr[fileNameArr.length-1];
            }
//            InputStream inputStream = FileUploadClient.getInputStream(file);
            String rootPath = ArcUtils.getRootTempPath();


            //创建文件
            File f = createFile(rootPath+fileBasePath, fileName);
            file.transferTo(f);
            String filePath = f.getPath();
            Map result = new HashMap<>();
            result.put("filePath",filePath);
            //TODO [微服务问题] 微服务环境不能本地存储

            result.put("excelHead",getExcelHead(filePath));
            return ResultBody.ok(result);
        } catch (IOException e) {
            log.error("上传文件错误",e);
            ApiAssert.failure("上传文件错误");
            return ResultBody.failed();
        }
    }
    //TODO 分布式部署存在文件找不到的问题
    @Override
    public List getExcelHead(String filePath) {
        try{
            List<Map> re =  getExcelInfo(filePath);
            Set keys = re.get(0).keySet();
            List headKeys = new ArrayList();
            //去掉excelRowNum
            for (Object key : keys) {
                if(!"excelRowNum".equals(key)){
                    headKeys.add(key);
                }
            }
            return headKeys;
        }catch (Exception e){
            log.error(String.format("导入档案异常 档案目录%s",filePath),e);
            ApiAssert.failure("导入档案异常,请检查导入模板格式");
        }

        return null;
    }
    //TODO 分布式部署存在文件找不到的问题
    @Override
    public List<Map> getExcelInfo(String filePath) {
        try{
            List<Map> re =  ExcelUtils.importExcel(filePath,0,1,Map.class);
            if(FlymeUtils.isEmpty(re)){
                ApiAssert.failure("导入的档案内容为空");
            }
            return re;
        }catch (Exception e){
            log.error(String.format("导入档案异常 档案目录%s,门类id=%s,机构id=%s",filePath),e);
            ApiAssert.failure("导入档案异常,请检查导入模板格式");
        }
        return null;
    }


    //TODO 分布式部署存在文件找不到的问题
    @Override
    public ResultBody check(String filePath, Long categoryId, Long unitId, String config) {
        ApiAssert.isNotEmpty("文件路径不能为空",filePath);
        ApiAssert.isNotEmpty("门类id不能为空",categoryId);
        ApiAssert.isNotEmpty("机构id不能为空",unitId);
        ApiAssert.isNotEmpty("配置信息不能为空",config);
        List<Map> excelData = getExcelInfo(filePath);
        List<ImportExcelMatchJson> configList = JsonUtils.json2list(config, ImportExcelMatchJson.class);
        ApiAssert.isNotEmpty("配置信息不能为空",configList);
        boolean hasMatchConfig =false;
        for(int i=0;i<configList.size();i++){
            ImportExcelMatchJson obj = configList.get(i);
            if(FlymeUtils.isNotEmpty(obj.getFieldId())&&FlymeUtils.isNotEmpty(obj.getMatchExcelHead())){
                hasMatchConfig = true;
                break;
            }
        }
        if(!hasMatchConfig){
            ApiAssert.failure("没有匹配的配置信息");
        }
        Map<String,ImportExcelMatchJson> configMap = configList.stream()
                .collect(Collectors.toMap(ImportExcelMatchJson::getMatchExcelHead,ImportExcelMatchJson->ImportExcelMatchJson));
        //获取门类字段
        CriteriaQuery<ArcField> fieldCriteriaQuery = new CriteriaQuery<ArcField>(ArcField.class);
        fieldCriteriaQuery.lambda().eq(ArcField::getCategoryId,categoryId);
        List<ArcField> list = arcFieldService.list(fieldCriteriaQuery);
        Map<Long,ArcField> fieldsMap = list.stream().collect(Collectors.toMap(ArcField::getFieldId,ArcField->ArcField));
        excelData.forEach(item->{
            item.forEach((key,value)->{
                ImportExcelMatchJson match = configMap.get(key);
                if(match!=null&&FlymeUtils.isNotEmpty(match.getMatchExcelHead())){
                    ArcField field = fieldsMap.get(match.getFieldId());
                    if(!"unitId".equals(field.getFieldName())) {
                        if (FlymeUtils.isNotEmpty(value)) {
                            if (field.getDataType().equals(2)) { //数字   int类型
                                if (!NumberUtil.isInteger(value.toString().trim())) {
                                    ApiAssert.failure(String.format("字段[%s]数据类型为数字,导入数据[%s]为非数字或过长"
                                            , field.getFieldCnName(), value));
                                }

                            } else {//字符串   校验长度
                                if (field.getInType().equals(2)) {
                                    if (!NumberUtil.isInteger(value.toString().trim())) {
                                        ApiAssert.failure(String.format("字段[%s]数据类型为数字,导入数据[%s]为非数字或过长"
                                                , field.getFieldCnName(), value));
                                    }

                                }
                                if (ArchiveEnumInteger.IS_FALSE.getCode().equals(field.getIsBlob())
                                        && field.getColumnLength() < value.toString().length()) {
                                    ApiAssert.failure(String.format("字段[%s]数据长度为[%s],导入数据[%s]过长"
                                            , field.getFieldCnName(), field.getColumnLength(), value));
                                }

                            }

                        } else if (FlymeUtils.isNotEmpty(match.getDefaultValue())) {//判断默认参数
                            if (field.getDataType().equals(2)) {//数字   int类型
                                if (!NumberUtil.isInteger(match.getDefaultValue())) {
                                    ApiAssert.failure(String.format("字段[%s]数据类型为数字,设置的默认值[%s]为非数字或过长"
                                            , field.getFieldCnName(), match.getDefaultValue()));
                                }
                            } else {//字符串   校验长度
                                if (field.getInType().equals(2)) {
                                    if (!NumberUtil.isInteger(value.toString().trim())) {
                                        ApiAssert.failure(String.format("字段[%s]数据类型为数字,导入数据[%s]为非数字或过长"
                                                , field.getFieldCnName(), value));
                                    }

                                }
                                if (ArchiveEnumInteger.IS_FALSE.getCode().equals(field.getIsBlob())
                                        && field.getColumnLength() < match.getDefaultValue().length()) {
                                    ApiAssert.failure(String.format("字段[%s]数据长度为[%s],设置的默认值[%s]过长"
                                            , field.getFieldCnName(), field.getColumnLength(), match.getDefaultValue()));
                                }
                            }
                        }
                    }

                }
            });
        });
        return ResultBody.ok();
    }

    @Override
    public ResultBody doImport(String filePath, Long categoryId, Long unidId, String config) {
        ApiAssert.isNotEmpty("文件路径不能为空",filePath);
        ApiAssert.isNotEmpty("门类id不能为空",categoryId);
        ApiAssert.isNotEmpty("机构id不能为空",unidId);
        ApiAssert.isNotEmpty("配置信息不能为空",config);
        List<Map> excelData = getExcelInfo(filePath);
        List<ImportExcelMatchJson> configList = JsonUtils.json2list(config, ImportExcelMatchJson.class);
        ApiAssert.isNotEmpty("配置信息不能为空",configList);
        boolean hasMatchConfig =false;
        for(int i=0;i<configList.size();i++){
            ImportExcelMatchJson obj = configList.get(i);
            if(FlymeUtils.isNotEmpty(obj.getFieldId())&&FlymeUtils.isNotEmpty(obj.getMatchExcelHead())){
                hasMatchConfig = true;
                break;
            }
        }
        if(!hasMatchConfig){
            ApiAssert.failure("没有匹配的配置信息");
        }
        Map<String,ImportExcelMatchJson> configMap = configList.stream()
                .collect(Collectors.toMap(ImportExcelMatchJson::getMatchExcelHead,ImportExcelMatchJson->ImportExcelMatchJson));
        //获取门类字段
        CriteriaQuery<ArcField> fieldCriteriaQuery = new CriteriaQuery<ArcField>(ArcField.class);
        fieldCriteriaQuery.lambda().eq(ArcField::getCategoryId,categoryId);
        List<ArcField> list = arcFieldService.list(fieldCriteriaQuery);
        Map<Long,ArcField> fieldsMap = list.stream().collect(Collectors.toMap(ArcField::getFieldId,ArcField->ArcField));
        ArcCategory category = this.arcCategoryService.getById(categoryId);
        Long importId = IdWorker.getId();


        Field[] declaredFields = ArcInfo.class.getDeclaredFields();
        Map<String,Field> declaredFieldMap = Arrays.stream(declaredFields).collect(Collectors.toMap(Field::getName, Field -> Field));
        List<ArcInfo> insertList = new ArrayList<>();
        excelData.forEach(item->{
            ArcInfo arcInfo = new ArcInfo();
            arcInfo.setCategoryId(categoryId);
            arcInfo.setUnitId(unidId);
            arcInfo.setQzId(category.getQzId());
            Map expandMap = new HashMap();
            item.forEach((key,value)->{
                ImportExcelMatchJson match = configMap.get(key);
                if(match!=null&&FlymeUtils.isNotEmpty(match.getMatchExcelHead())){
                    ArcField field = fieldsMap.get(match.getFieldId());
                    String setValue = null;
                    if(FlymeUtils.isNotEmpty(value)){
                        setValue = value.toString().trim();
                    }else{
                        setValue = match.getDefaultValue();
                    }
                    if(FlymeUtils.isNotEmpty(setValue)&&(!"unitId".equals(field.getFieldName())) ){
                        if (2 == field.getDataType().intValue()) {//数字   int类型
                            if (!NumberUtil.isInteger(setValue)) {
                                ApiAssert.failure(String.format("字段[%s]数据类型为数字,导入数据[%s]为非数字或过长"
                                        , field.getFieldCnName(), setValue));
                            } else {
                                if (ArchiveEnumInteger.IS_TRUE.getCode().equals(field.getIsBlob())) {
                                    expandMap.put(field.getFieldName(), setValue);
                                } else {
                                    ReflectionUtils.setFieldValue(arcInfo, field.getFieldName(),
                                            arcInfoService.changeFieldValueType(
                                                    arcInfoService.getFieldType(declaredFieldMap.get(field.getFieldName())
                                                            ,"Integer")
                                                    ,setValue));
                                }
                            }
                        }else{//字符串   校验长度
                            if(ArchiveEnumInteger.IS_TRUE.getCode().equals(field.getIsBlob())){
                                expandMap.put(field.getFieldName(),setValue);
                            }else{
                                ReflectionUtils.setFieldValue(arcInfo,field.getFieldName()
                                        ,arcInfoService.changeFieldValueType(
                                                arcInfoService.getFieldType(declaredFieldMap.get(field.getFieldName())
                                                ,"String")
                                        ,setValue));
                            }
                        }
                    }
                }
            });

            arcInfoService.initArcInfoPreField(arcInfo);
            arcInfo.setStatus(ArcStatusEnum.ARC_STATUS_0.getCode());
            arcInfo.setExpand(JsonUtils.beanToJson(expandMap));
            arcInfo.setOutSideId(importId);
            arcInfo.setOriginalCount(0);
            insertList.add(arcInfo);
//            arcInfoService.save(arcInfo);
        });
        List<ArcInfo> batchInsert = new ArrayList<>();
        for(int i=0;i<insertList.size();i++){
            batchInsert.add(insertList.get(i));
            if(batchInsert.size()>=500){
                arcInfoService.saveBatch(batchInsert);
                batchInsert.clear();
            }else if(i==(insertList.size()-1)&&batchInsert.size()>0){
                arcInfoService.saveBatch(batchInsert);
            }
        }



        ArcImport arcImport = new ArcImport();
        arcImport.setImportId(importId);
        arcImport.setQzId(category.getQzId());
        arcImport.setCategoryId(categoryId);
        arcImport.setCategoryName(category.getCnName());
        arcImport.setFileName(filePath.substring(filePath.lastIndexOf(File.separator)) + 1);
        arcImport.setUnitId(unidId);

        //TODO 设置机构名称
        //arcImport.setUnitName();
        SysDept dept = sysDeptService.getById(unidId);
        if(FlymeUtils.isNotEmpty(dept)){
            arcImport.setUnitName(dept.getDeptName());
        }
        arcImport.setCount(excelData.size());
        arcImport.setFilePath(filePath);

        arcImport.setCategoryId(categoryId);
        this.save(arcImport);


        return ResultBody.ok();
    }

    @Override
    public ResultBody back(Long importId) {
        ApiAssert.isNotEmpty("导入记录id不能为空",importId);
        ArcImport arcImport = this.getById(importId);
        ApiAssert.isNotEmpty("无法查询到导入记录",arcImport);

        //将档案加入回收站
//        ArcInfo recycleDel = new ArcInfo();
//        recycleDel.setIsRecycle(ArchiveEnumInteger.IS_TRUE.getCode());
//        recycleDel.setUpdateTime(new Date());
//        recycleDel.setUpdateBy(OpenHelper.getUser().getNickName());
//        recycleDel.setStatus(ArcStatusEnum.ARC_STATUS_0.getCode());
//
//
//        CriteriaUpdate<ArcInfo> recycleDelUpdate = new CriteriaUpdate<ArcInfo>();
//        recycleDelUpdate.lambda().eq(ArcInfo::getCategoryId,arcImport.getCategoryId())
//                .eq(ArcInfo::getOutSideId,arcImport.getImportId());

        LambdaUpdateWrapper<ArcInfo> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(ArcInfo::getCategoryId,arcImport.getCategoryId())
                .eq(ArcInfo::getOutSideId,arcImport.getImportId())
                .set(ArcInfo::getIsRecycle,ArchiveEnumInteger.IS_TRUE.getCode())
                .set(ArcInfo::getUpdateTime,new Date())
                .set(ArcInfo::getUpdateBy,OpenHelper.getUser().getNickName())
                .set(ArcInfo::getStatus,ArcStatusEnum.ARC_STATUS_0.getCode())
                .set(ArcInfo::getOutSideId,null);

        this.arcInfoService.update(updateWrapper);

        //删除记录
        this.removeById(arcImport.getImportId());

        return ResultBody.ok();
    }

    @Override
    public void exportTmp(Long categoryId, HttpServletRequest request, HttpServletResponse response) {
        try {
            ArcCategory arcCategory = arcCategoryService.getById(categoryId);
            CriteriaQuery<ArcField> fieldCriteriaQuery = new CriteriaQuery<ArcField>(ArcField.class);
            fieldCriteriaQuery.lambda().eq(ArcField::getCategoryId,categoryId);
            List<ArcField> list = arcFieldService.list(fieldCriteriaQuery);


            List<ExcelExportEntity> entityList = list.stream().filter(item->{
                return !"unitId".equals(item.getFieldName())&&ArchiveEnumInteger.IS_TRUE.getCode().equals(item.getInShow());
            }).sorted(
                    Comparator.comparing(ArcField::getInSeq)
            ).map(item->{
                ExcelExportEntity obj = new ExcelExportEntity();
                obj.setKey(item.getFieldName());
                obj.setName(item.getFieldCnName());
                obj.setStatistics(false);
                return obj;
            }).collect(Collectors.toList());

            String fileName = "Excel导入模板-"+arcCategory.getCnName();
            MyExportParams exportParams = new MyExportParams(fileName, null, "", ExcelType.XSSF);
            Workbook workbook = ExcelExportUtil.exportExcel(exportParams, entityList, new ArrayList<>());

            response.setHeader("content-disposition", WebFilenameUtils.disposition(fileName+".xlsx"));
            ServletOutputStream out = response.getOutputStream();
            workbook.write(out);
            out.flush();
        } catch (Exception var27) {
            var27.printStackTrace();
        }
    }

    private File createFile(String path, String fileName) {
        File dir = new File(path);
        if (!dir.exists()) {
            //创建文件夹
            dir.mkdirs();
        }
        File f = new File(path + "/" + fileName);
        if (!f.getParentFile().exists()) {
            f.getParentFile().mkdirs();
        }
        ApiAssert.isFalse("文件名已存在", f.exists());
        return f;

    }


    public static void main(String[] args) {

        List<Map> re = ExcelUtils.importExcel("/Users/mhsdong/Downloads/发文.xlsx", 0, 1, Map.class);
        System.out.println();
    }
}
